﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;

namespace LightEngine
{
    namespace MessbauerCore
    {
        namespace MessbauerData
        {
            /// <Описание перечисления public enum SpectrumFileFormats>
            /// Перечисление содержит возможные типы форматов файлов мессбауэровских спектров:
            /// Bin - двоичный формат, в этом формате на каждую точку отводится 3 байта, записанных
            /// в Intell-порядке (младшими байтами вперед), между точками стоят байты-разделители
            /// (0-байт).
            /// Txt - Точки спектра записаны текстовыми символами в ANSI-кодировке между точками вписаны
            /// символы-разделители (пробелы), в конце каждой строки должно быть \r\n .
            /// </Описание перечисления public enum SpectrumFileFormats>
            public enum SpectrumFileFormats
            {
                Bin = 1,
                Txt = 2
            }

            /// <описание класса public class MessbSpectrumLoader>
            /// Версия класса 1.0
            /// Автор(ы): Ушаков Михаил
            /// Класс предназначен для загрузки и сохранения мессбауэровских спектров из/в файл(ов/ы)
            /// в двух форматах двоичном и текстовом.
            /// 1. Константы класса:
            /// а) private const UInt16 NumOfWordsToAnalyze = 20; число прочтенных байт для анализа
            ///    является ли формат файла спектра двоичным.
            /// б) private const UInt16 PointsInString = 10; число записываем точек спектра в строке файла с
            ///    текстовым форматом, сделано для совместимости с Univem MS.
            /// в) private const UInt16 NumOfBytesInMemoryCell = 3; число байт на точку спектра в файлах
            ///    с двоичным форматом.
            /// 2. Поля класса:
            /// а) private static Boolean SuccessfullyLoaded = false; результат последней операции загрузки
            ///    мессбауэровского спектра из файла (если равно true значит загрузить спектр удалось).
            /// б) private static UInt16 SpecFileType = 0; тип загружаемого спектра, может принимать значения
            ///    из SpectrumFileFormats.
            /// 3. Методы класса:
            /// а) private static SpectrumFileFormats ParseSpectrumFile(FileStream SourceFile) определение типа
            ///    спектра загружаемого из потока (в данном случае указывается полный путь к файлу) 
            ///    SourceFile.
            /// б) private static void LoadSpectrumFromBinaryFile(FileStream SourceFile, MessbSpectrum Spec)
            ///    загрузка спектра в двоичном формате из файла с абсолютным путем SourceFile в объект Spec.
            /// в) private static void LoadSpectrumFromTextFile(FileStream SourceFile, MessbSpectrum Spec)
            ///    загрузка спектра в текстовом формате из файла с абсолютным путем SourceFile в объект Spec.
            /// г) public static void LoadSpectrumFromFile(String Source, MessbSpectrum Spec) функция загрузки
            ///    мессбауэровского спектра из файла с путем Source в объект Spec, функция вызывает определитель
            ///    типа файла ParseSpectrumFile и в соотвествии с возвращаемым этой функцией результатом вызывает
            ///    либо LoadSpectrumFromBinaryFile либо LoadSpectrumFromTextFile.
            /// д) public static Boolean IsSuccessfullyLoaded() возвращает результат
            ///    загрузки последнего файла спектра.
            /// е) public static UInt16 GetTypeOfLastLoadedSpectrum() возвращает тип из SpecTransformResult
            ///    последнего загруженного файла спектра.
            /// ж) public static void LoadSpectrumToFile(String Source, MessbSpectrum Spec) сохренение спектра
            ///    в файл, позволяет сохранять в файлы с расширением .dat, .spc, . asc .
            /// з) private static void SaveSpectrumToDataFile(FileStream SourceFile, MessbSpectrum Spec) 
            ///    сохранение спектра в .dat-файл в виде 2х столбцов номер канала - число импульсов.
            /// и) private static void SaveSpectrumToTextFile(FileStream SourceFile, MessbSpectrum Spec)
            ///    сохранение спектра в текстовом формате по PointsInString точек в строке.
            /// к) private static void SaveSpectrumToBinaryFile(FileStream SourceFile, MessbSpectrum Spec)
            ///    сохранение спектра в двоичном формате.
            /// 4. Свойства и индексаторы класса:
            ///    отсутствуют. 
            /// </описание класса public class MessbSpectrumLoader>
            public class MessbSpectrumLoader
            {
                private const UInt16 NumOfWordsToAnalyze = 20;
                private const UInt16 PointsInString = 10;
                private const UInt16 NumOfBytesInMemoryCell = 3;
                private static Boolean SuccessfullyLoaded = false;
                private static UInt16 SpecFileType = 0;

                private static SpectrumFileFormats ParseSpectrumFile(FileStream SourceFile)
                {
                    Boolean ByteMode = false;
                    using (BinaryReader BytesReader = new BinaryReader(SourceFile))
                    {
                        Byte[] BytesArr = new Byte[NumOfWordsToAnalyze];
                        BytesReader.Read(BytesArr, 0, BytesArr.Length);
                        for (UInt16 Counter = NumOfBytesInMemoryCell; Counter < BytesArr.Length; )
                        {
                            // После 3х байт информации отступ - один нулевой байт
                            if (BytesArr[Counter] == 0)
                            {
                                ByteMode = true;
                            }
                            else
                            {
                                ByteMode = false;
                            }
                            Counter += (NumOfBytesInMemoryCell + 1);
                        }
                    }
                    if (ByteMode == true)
                    {
                        return SpectrumFileFormats.Bin;
                    }
                    return SpectrumFileFormats.Txt;
                }
                private static void LoadSpectrumFromTextFile(FileStream SourceFile, MessbSpectrum Spec)
                {
                    using (StreamReader SpectrumReader = new StreamReader(SourceFile))
                    {
                        UInt16 Counter = 0;
                        UInt64[] Spectrum = new UInt64[(UInt16)AllowableNumOfChannels.Channel_Mode5];
                        UInt64 TempValue = 0;
                        Boolean Result = false;

                        while (true)
                        {
                            string Line = SpectrumReader.ReadLine();
                            if (Line == null)
                                break;

                            string[] NumberInLines = Line.Split(' ');

                            foreach (string Number in NumberInLines)
                            {
                                if (string.IsNullOrEmpty(Number) || string.IsNullOrEmpty(Number.Trim()))
                                    continue;
                                try
                                {
                                    TempValue = UInt64.Parse(Number);
                                    Result = true;
                                }
                                catch (Exception AllExceptions)
                                {
                                    AllExceptions.ToString();
                                    Result = false;
                                    break;
                                }
                            }

                            if (Result == false) continue;

                            foreach (string Number in NumberInLines)
                            {
                                if (string.IsNullOrEmpty(Number) || string.IsNullOrEmpty(Number.Trim()))
                                    continue;
                                TempValue = UInt64.Parse(Number);
                                Spectrum[Counter] = TempValue;
                                Counter++;
                            }
                        }
                        // проверка на соответсвие Counter поддерживаемому числу каналов
                        AllowableNumOfChannels Check = (AllowableNumOfChannels)Counter;
                        if (Enum.IsDefined(typeof(AllowableNumOfChannels), Check))
                        {
                            Spec.LoadSpectrum(Spectrum, Counter);
                            SuccessfullyLoaded = true;
                        }
                        else SuccessfullyLoaded = false;
                    }
                }

                private static void LoadSpectrumFromBinaryFile(FileStream SourceFile, MessbSpectrum Spec)
                {
                    UInt16 Counter = 0;
                    UInt64[] Spectrum = new UInt64[(UInt16)AllowableNumOfChannels.Channel_Mode5];
                    UInt64 TempValue = 0, SymbRed = 0;
                    using (BinaryReader BytesReader = new BinaryReader(SourceFile))
                    {
                        Byte[] OnePoint = new Byte[NumOfBytesInMemoryCell];
                        Byte[] OneSymb = new Byte[1];
                        while (true)
                        {
                            SymbRed = (UInt16)BytesReader.Read(OnePoint, 0, OnePoint.Length);

                            if (SymbRed == (UInt16)OnePoint.Length)
                            {
                                for (UInt16 Count = 0; Count < OnePoint.Length; Count++)
                                {
                                    // в KAMAK формате байты записаны младшими битами вперед (MOTOROLLA)
                                    TempValue += (UInt64)(OnePoint[Count]) << Count * 8;
                                }
                                Spectrum[Counter] = TempValue;
                                TempValue = 0;
                                Counter++;
                                // проверка отступа
                                BytesReader.Read(OneSymb, 0, 1);
                                if (OneSymb[0] != 0)
                                {
                                    break;
                                }
                            }
                            else break;
                        }
                        // проверка на соответсвие Counter поддерживаемому числу каналов
                        AllowableNumOfChannels Check = (AllowableNumOfChannels)Counter;
                        if (Enum.IsDefined(typeof(AllowableNumOfChannels), Check))
                        {
                            Spec.LoadSpectrum(Spectrum, Counter);
                            SuccessfullyLoaded = true;
                        }
                        else SuccessfullyLoaded = false;
                    }
                }

                public static void LoadSpectrumFromFile(String Source, MessbSpectrum Spec)
                {
                    FileStream File = new FileStream(Source, FileMode.Open,
                                                     FileAccess.Read, FileShare.None);
                    UInt16 FileType = (UInt16)ParseSpectrumFile(File);
                    SuccessfullyLoaded = false;
                    SpecFileType = FileType;
                    // парсинг закрывает имеющийся поток, открывыаем его снова!
                    File = new FileStream(Source, FileMode.Open,
                                          FileAccess.Read, FileShare.None);
                    if (FileType == (UInt16)SpectrumFileFormats.Txt)
                        LoadSpectrumFromTextFile(File, Spec);
                    else LoadSpectrumFromBinaryFile(File, Spec);
                }

                public static Boolean IsSuccessfullyLoaded() { return SuccessfullyLoaded; }
                public static UInt16 GetTypeOfLastLoadedSpectrum() { return SpecFileType; }

                public static void LoadSpectrumToFile(String Source, MessbSpectrum Spec)
                {
                    FileStream File = new FileStream(Source, FileMode.Create,
                                                       FileAccess.Write, FileShare.None);
                    if (Source.ToLower().EndsWith(".spc") == true)
                    {
                        //сохраняем файл в аппаратном виде, т.е.
                        //Byte0 Byte1 Byte2 0-Byte
                        SaveSpectrumToBinaryFile(File, Spec);
                    }
                    else if (Source.ToLower().EndsWith(".dat") == true)
                    {
                        SaveSpectrumToDataFile(File, Spec);
                    }
                    else
                    {
                        // сохраняем в текстовом
                        SaveSpectrumToTextFile(File, Spec);
                    }
                }

                private static void SaveSpectrumToDataFile(FileStream SourceFile, MessbSpectrum Spec)
                {
                    using (StreamWriter SpectrumWriter = new StreamWriter(SourceFile))
                    {
                        for (UInt16 Counter = 0; Counter < Spec.NumOfChannels; Counter++)
                        {
                            SpectrumWriter.Write(Spec[Counter]);
                            SpectrumWriter.Write(Environment.NewLine);//"\r\n");
                        }
                    }
                }

                private static void SaveSpectrumToTextFile(FileStream SourceFile, MessbSpectrum Spec)
                {
                    UInt16 Counter = 0;
                    using (StreamWriter SpectrumWriter = new StreamWriter(SourceFile))
                    {
                        while (true)
                        {
                            for (UInt16 Series = 0; Series < PointsInString; Series++)
                            {
                                if (Counter == Spec.NumOfChannels)
                                {
                                    SpectrumWriter.Write(Environment.NewLine);
                                    return;
                                }
                                SpectrumWriter.Write(" ");
                                SpectrumWriter.Write(Spec[Counter]);
                                Counter++;
                            }
                            SpectrumWriter.Write(Environment.NewLine);
                        }

                    }
                }

                private static void SaveSpectrumToBinaryFile(FileStream SourceFile, MessbSpectrum Spec)
                {
                    UInt16 Counter = 0;
                    using (BinaryWriter SpectrumWriter = new BinaryWriter(SourceFile))
                    {
                        while (true)
                        {
                            Byte[] OnePoint = new Byte[NumOfBytesInMemoryCell];
                            for (UInt16 Series = 0; Series < PointsInString; Series++)
                            {
                                if (Counter == Spec.NumOfChannels)
                                {
                                    return;
                                }
                                for (UInt16 Count = 0; Count < OnePoint.Length; Count++)
                                {
                                    OnePoint[Count] = (Byte)((Spec[Counter] & (UInt64)(0xFF << Count * 8)) >> Count * 8);
                                    //MessageBox.Show((OnePoint[Count].ToString()));
                                }
                            }
                            SpectrumWriter.Write(OnePoint);
                            SpectrumWriter.Write((Byte)0);
                            Counter++;
                        }
                    }
                }
            }
        }
    }
}
